package com.atldy.commerce.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.atldy.commerce.async.impl.AsyncTaskManager;
import com.atldy.commerce.commence.Id;
import com.atldy.commerce.commence.TableId;
import com.atldy.commerce.commence.goods.DeductGoodsInventory;
import com.atldy.commerce.commence.goods.GoodsInfo;
import com.atldy.commerce.commence.goods.SimpleGoodsInfo;
import com.atldy.commerce.constant.GoodsConstant;
import com.atldy.commerce.mapper.GoodsMapper;
import com.atldy.commerce.pojo.Goods;
import com.atldy.commerce.service.GoodsService;
import com.atldy.commerce.utlis.RedisUtils;
import com.atldy.commerce.vo.PageSimpleGoodsInfo;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 *
 */
@Service
@Slf4j
public class GoodsServiceImpl extends ServiceImpl<GoodsMapper, Goods> implements GoodsService {

    @Autowired
    private AsyncTaskManager asyncTaskManager;

    @Autowired
    private RedisUtils redisUtils;


    @Autowired
    private GoodsMapper goodsMapper;

    @Override
    public List<GoodsInfo> getGoodsInfos(TableId tableId) {
        List<Long> ids = tableId.getIds().stream()
                .map(Id::getId)
                .collect(Collectors.toList());
        List<Goods> goods = new LambdaQueryChainWrapper<Goods>(goodsMapper)
                .in(Goods::getId, ids).list();
        List<GoodsInfo> goodsInfos = goods.stream()
                .map(Goods::toGoodsInfo)
                .collect(Collectors.toList());
        return goodsInfos;
    }

    @Override
    public PageSimpleGoodsInfo getPageSimpleGoodsInfos(int page) {
        PageSimpleGoodsInfo goodsInfo = new PageSimpleGoodsInfo();
        if (page <= 1) {
            //从第一也开始,默认一页包含十条
            page = 0;
        }
        Page<Goods> goodsPage = new Page<>(page, 10);
        Page<Goods> Goodspage = new LambdaQueryChainWrapper<>(goodsMapper)
                .orderByDesc(Goods::getCreateTime)
                .page(goodsPage);
        List<SimpleGoodsInfo> simpleGoodsInfos = Goodspage.getRecords().stream()
                .map(Goods::toSimple)
                .collect(Collectors.toList());
        goodsInfo.setSimpleGoodsInfos(simpleGoodsInfos);
        log.info("总页数是：[{}]", Goodspage.getPages());
        goodsInfo.setHasMore(Goodspage.getPages() > page);
        return goodsInfo;
    }

    @Override
    public List<SimpleGoodsInfo> getSimpleGoodsInfos(TableId tableId) {
        List<Object> ids = tableId.getIds().stream()
                .map(Id::getId).collect(Collectors.toList());
        ids = ids.stream().map(Convert::toStr).collect(Collectors.toList());
        //从redis中取出信息
        List<Object> objects
                = redisUtils.hmultiGet(GoodsConstant.ECOMMERCE_GOODS_DICT_KEY, ids)
                .stream().filter(Objects::nonNull).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(objects)) {
            List<SimpleGoodsInfo> simpleGoodsInfos = parseCacheGoodsInfo(objects);
            log.info("从redis中取出商品信息：[{}]", JSONUtil.formatJsonStr(
                    JSONUtil.toJsonStr(simpleGoodsInfos)));
            if (objects.size() == ids.size()) {
                log.info("商品信息全部从redis获取成功>>>>>");
                return simpleGoodsInfos;
            } else {
                List<SimpleGoodsInfo> right = simpleGoodsInfos;
                //取出商品id的差集,进行查询
                List<Long> rightIds = simpleGoodsInfos.stream().map(SimpleGoodsInfo::getId).collect(Collectors.toList());
                List<Long> leftIds = ids.stream().map(Convert::toLong).collect(Collectors.toList());
                List<Long> longs = CollectionUtil.subtract(leftIds, rightIds).stream().collect(Collectors.toList());
                log.info("计算获取的差集ID集合是：[{}]", longs.stream().toArray());
                //计算redis中缺失的商品
                List<SimpleGoodsInfo> left = queryGoodsInfoFormDbAndCacheToRedis(new TableId(
                        longs.stream().map(Id::new)
                                .collect(Collectors.toList())));
                log.info("获取缺失部分：[{}]", JSONUtil.formatJsonStr(
                        JSONUtil.toJsonStr(left)
                ));
                return new ArrayList<>(CollectionUtil.union(right, left));
            }
        }
        return queryGoodsInfoFormDbAndCacheToRedis(tableId);
    }

    @Override
    public Boolean deductGoodsInventory(List<DeductGoodsInventory> deductGoodsInventory) {

        deductGoodsInventory.stream()
                .forEach(deasctt -> {
                    if (deasctt.getCount() <= 0) {
                        throw new RuntimeException("需要扣减的库存数量需要大于0");
                    }
                });
        List<Long> goodsId = deductGoodsInventory.stream()
                .map(DeductGoodsInventory::getGoodsId).collect(Collectors.toList());
        List<Goods> goodsList = new LambdaQueryChainWrapper<>(goodsMapper)
                .in(Goods::getId, goodsId)
                .list();
        if (CollectionUtil.isEmpty(goodsList)) {
            throw new RuntimeException("查询到商品不存在");
        }

        Map<Long, DeductGoodsInventory> goodsInventoryMap = deductGoodsInventory.stream()
                .collect(Collectors.toMap(DeductGoodsInventory::getGoodsId, Function.identity()));

        goodsList.stream()
                .forEach(goods -> {
                    Long goodsInventory = goods.getInventory();
                    DeductGoodsInventory inventory = goodsInventoryMap.get(goods.getId());
                    if (inventory.getCount() > goodsInventory) {
                        throw new RuntimeException("库存数量不足!");
                    }
                    // 扣减库存
                    goods.setInventory(goodsInventory - inventory.getCount());
                });

        boolean flag = false;
        //更新
        for (Goods goods : goodsList) {
            boolean update = new LambdaUpdateChainWrapper<Goods>(goodsMapper)
                    .eq(Goods::getId, goods.getId())
                    .update(goods);
            flag = update;
        }
        return flag;
    }

    /**
     * 将缓存中单数据反序列化成Java中的pojo SimpleGoodsInfo 对象
     */
    private List<SimpleGoodsInfo> parseCacheGoodsInfo(List<Object> cacheGoodsInfos) {
        if (CollectionUtil.isEmpty(cacheGoodsInfos)) {
            return null;
        }
        List<SimpleGoodsInfo> list = cacheGoodsInfos.stream()
                .map(o -> JSON.parseObject(o.toString(), SimpleGoodsInfo.class))
                .collect(Collectors.toList());
        return list;
    }

    /**
     * 从缓存中取出存储的商品信息数据
     */
    private List<SimpleGoodsInfo> queryGoodsInfoFormDbAndCacheToRedis(TableId tableId) {
        List<Long> ids = tableId.getIds().stream()
                .map(Id::getId)
                .collect(Collectors.toList());

        List<Goods> goods = new LambdaQueryChainWrapper<Goods>(goodsMapper)
                .in(Goods::getId, ids).list();

        List<SimpleGoodsInfo> simpleGoodsInfos = goods.stream()
                .map(Goods::toSimple)
                .collect(Collectors.toList());
        //存入redis
        Map<String, Object> result = new HashMap<>();
        simpleGoodsInfos.stream()
                .forEach(simpleGoodsInfo ->
                        result.put(simpleGoodsInfo.getId().toString(), JSON.toJSONString(simpleGoodsInfo)));
        redisUtils.hmset(GoodsConstant.ECOMMERCE_GOODS_DICT_KEY, result);
        log.info("从MySQL中取出商品信息并存储到redis：[{}]", JSONUtil.formatJsonStr(
                JSONUtil.toJsonStr(simpleGoodsInfos)));
        return simpleGoodsInfos;
    }
}




